import io
import os
import time
import tkinter as tk
from grid import Grid, Cell
from color import *
from PIL import Image
from puzzlecell import PuzzleCell
from tip import TipTool


class Puzzle:
    def __init__(self, container, grid: Grid):
        self.grid = grid
        self.history = []

        self.cellSize = PuzzleCell.cellSize
        self.margin = PuzzleCell.margin
        canvasSize = self.cellSize * 9 + self.margin * 2

        self.canvas = tk.Canvas(
            container, width=canvasSize, height=canvasSize, bg="white"
        )
        self.canvas.pack(side=tk.LEFT, padx=10, pady=10)

        self.valueFont = PuzzleCell.valueFont
        self.candFont = PuzzleCell.candFont
        self.baseCells: list[list[PuzzleCell]] = [
            [PuzzleCell(self.canvas, i, j) for j in range(9)] for i in range(9)
        ]

        self.tippedWidgets = []

        self.draw_plate()
        self.draw_labels()
        self.draw_grid()

        self.canvas.bind("<Button-1>", self.chooseValue)
        self.canvas.bind("<Control-Button-1>", self.removeCand)

        self.tipTool = TipTool(self)

    def back(self):
        if not self.history:
            return

        previous_state = self.history[-1]
        self.history = self.history[:-1]

        self.grid.decode(previous_state.encode())
        self.grid.init()
        self.grid.clearup()
        self.draw_grid()
        self.turnoffTippedWidgets()
        self.tipTool.tip_init()

    def chooseValue(self, event):
        ok, row, col, cand = self.clickCand(event)
        if not ok:
            return

        self.save_state()

        self.grid.solveOne(row, col, cand)
        self.draw_grid()
        self.turnoffTippedWidgets()
        self.tipTool.tip_init()

    def clickCand(self, event) -> tuple[bool, int, int, int]:
        col = (event.x - self.margin) // self.cellSize
        row = (event.y - self.margin) // self.cellSize

        if not (0 <= row < 9 and 0 <= col < 9):
            return False, 0, 0, 0

        grid_cell = self.grid.rows[row][col]

        if grid_cell.value != 0:
            return False, 0, 0, 0

        rel_x = (event.x - self.margin) - col * self.cellSize
        rel_y = (event.y - self.margin) - row * self.cellSize

        cand_col = rel_x // (self.cellSize // 3)
        cand_row = rel_y // (self.cellSize // 3)
        cand_num = cand_row * 3 + cand_col + 1

        ok = 1 <= cand_num <= 9 and cand_num in grid_cell.cands
        return ok, row, col, cand_num

    def draw_grid(self):
        for rowNum in range(9):
            for colNum in range(9):
                c: Cell = self.grid.rows[rowNum][colNum]
                bc: PuzzleCell = self.baseCells[rowNum][colNum]
                if c.value != 0:
                    bc.setValue(c.value, c.given)
                else:
                    bc.setCands(c.cands)

    def draw_labels(self):
        for i in range(9):
            x = self.margin + i * self.cellSize + self.cellSize // 2

            self.canvas.create_text(
                x,
                self.margin // 2,
                text=str(i + 1),
                font=("Arial", 12),
                fill="#A0A0A0",
            )

            self.canvas.create_text(
                x,
                self.margin + 9 * self.cellSize + self.margin // 2,
                text=str(i + 1),
                font=("Arial", 12),
                fill="#A0A0A0",
            )

        for i in range(9):
            y = self.margin + i * self.cellSize + self.cellSize // 2

            self.canvas.create_text(
                self.margin // 2,
                y,
                text=str(i + 1),
                font=("Arial", 12),
                fill="#A0A0A0",
            )

            self.canvas.create_text(
                self.margin + 9 * self.cellSize + self.margin // 2,
                y,
                text=str(i + 1),
                font=("Arial", 12),
                fill="#A0A0A0",
            )

    def draw_plate(self):
        self.canvas.create_rectangle(
            self.margin,
            self.margin,
            self.margin + 9 * self.cellSize,
            self.margin + 9 * self.cellSize,
            outline="black",
            width=3,
        )
        for i in range(1, 9):
            x = self.margin + i * self.cellSize
            y = self.margin + i * self.cellSize

            line_width = 3 if i % 3 == 0 else 1
            self.canvas.create_line(
                x,
                self.margin,
                x,
                self.margin + 9 * self.cellSize,
                width=line_width,
                fill="black",
            )

            self.canvas.create_line(
                self.margin,
                y,
                self.margin + 9 * self.cellSize,
                y,
                width=line_width,
                fill="black",
            )

    def image(self):
        w = self.cellSize * 9 + self.margin * 2
        h = w

        scaling_factor = 3
        data = self.canvas.postscript(
            colormode="color",
            width=w,
            height=h,
            pagewidth=w * 10,
            pageheight=h * 10,
        )

        data_bytes = io.BytesIO(data.encode("utf-8"))
        img = Image.open(data_bytes)
        img = img.resize(
            (w * 2, h * 2),
        )
        current_dir = os.path.dirname(os.path.abspath(__file__))
        png_dir = os.path.join(current_dir, "../png")
        t = time.strftime("%y%m%d%H%M%S", time.localtime())
        filename = f"sudoku{t}.png"
        filename = os.path.join(png_dir, filename)
        img.save(
            filename,
            quality=95,
            dpi=(300, 300),
        )

    def init_history(self):
        self.history = []

    def removeCand(self, event):
        ok, row, col, cand = self.clickCand(event)
        if not ok:
            return

        self.save_state()

        cell = self.baseCells[row][col]
        self.canvas.itemconfig(cell.candCircles[cand - 1], state="hidden")

        self.grid.removeCand(row, col, cand)
        self.draw_grid()
        self.tipTool.tip_init()

    def save_state(self):
        grid_copy = Grid()
        grid_copy.decode(self.grid.encode())
        self.history.append(grid_copy)

    def turnoffTippedWidgets(self):
        for x in self.tippedWidgets:
            self.canvas.itemconfig(x, state="hidden")
        self.tippedWidgets = []
